package com.lee.sentinelorder.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.lee.sentinelorder.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * <p> Title: $file.className</p>
 *
 * @基本功能:
 * @program:SpringCloudDemo
 * @author: CareLee
 * @create:2021-07-31 12:06:21
 * Copyright: Copyright (c) 2040 CareLee. All rights reserved.
 **/
@RestController
@RefreshScope
@Slf4j
public class SentinelController {
    private static final String RESOURCE = "hello";
    private static final String USER_RESOURCE_NAME = "name";

    @Value("${base.name}")
    String name;
    @Value("${base.age}")
    String age;

    @RequestMapping("/hello")
    public String hello() {
        Entry entry = null;
        try {
            entry = SphU.entry(RESOURCE);
            String str = "hello world";
            log.info("+++++++++++" + str + "++++++++++++");
            return str;
        } catch (BlockException e1) {
            log.info("block!");
            return "被流控了!";
        } finally {
            if (null != entry)
                entry.exit();
        }
    }

    /**
     *
     * // 限流与阻塞处理
     * @SentinelResource 改善接口中资源定义和被流控降级后的处理方法
     * 怎么使用, 1.添加依赖<artifactId>sentinel-annotation-aspectj</artifactId>
     *          2.配置 bean-SentinelResouceAspect
     *      value 定义资源
     *      blockHandler 设置 流控降级后的处理方法(默认该方法必须声明在同一个类中)
     *                  如果不想在同一个类中blockHanderClass 但是方法必须是static
     *      fallback 当前接口出现了异常,就可以交给fallback指定的方法进行处理
     *                如果不想在同一个类中 fallbackClass但是方法必须是static
     *      blockHandler 如果和fallback同时指定了,则blockHandler优先级更高
     * @param
     * @return
     */
    @GetMapping("/name")
    @SentinelResource(value = USER_RESOURCE_NAME, blockHandler = "blockHandlerForGetUser")
    public User getName() {
        User res = new User(name,age);
        log.info(res.toString());
        return res;
    }

    @GetMapping("/age")
    public User getAge() {
        User res = new User(name,age);
        log.info(res.toString());
        return res;
    }



    /**
     * spring 的初始化方法
     */
    @PostConstruct //
    private static void initFlowRules() {
        // 流控规则
        List<FlowRule> rules = new ArrayList<>();
        // 流控
        FlowRule rule = new FlowRule();
        // 设置受保护的资源, 为哪个资源设置流控
        rule.setResource(RESOURCE);
        // 设置流控规则QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阀值
        // Set limit QPS to 20;
        rule.setCount(1);
        rules.add(rule);

        // 流控
        FlowRule rule2 = new FlowRule();
        // 设置受保护的资源, 为哪个资源设置流控
        rule2.setResource(USER_RESOURCE_NAME);
        // 设置流控规则QPS
        rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阀值
        // Set limit QPS to 20;
        rule2.setCount(1);
        rules.add(rule2);
        FlowRuleManager.loadRules(rules);

    }

    /**
     *
     * 注意:
     *   1.一定是public
     *   2. 返回值一定要和资源方法保持一致
     *
     * @param ex
     * @return
     */
    public User blockHandlerForGetUser(BlockException ex){
        log.info("流控!!! xian");
        ex.printStackTrace();
        return new User("流控!!! xian");
    }

}
